package com.lzh.db.handler;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.core.handlers.StrictFill;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import org.apache.ibatis.reflection.MetaObject;

import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Supplier;

/**
 * @author zhehen.lu
 * @date 2025/8/15 11:03
 */
public interface LzhMetaObjectHandler {
    default boolean openInsertFill() {
        return true;
    }

    default boolean openUpdateFill() {
        return true;
    }

    void insertFill(MetaObject object);

    void updateFill(MetaObject object);

    default LzhMetaObjectHandler setFieldValByName(String fieldName, Object fieldVal, MetaObject metaObject) {
        if (Objects.nonNull(fieldVal) && metaObject.hasSetter(fieldName)) {
            metaObject.setValue(fieldName, fieldVal);
        }

        return this;
    }

    default Object getFieldValByName(String fieldName, MetaObject metaObject) {
        return metaObject.hasGetter(fieldName) ? metaObject.getValue(fieldName) : null;
    }

    /** @deprecated */
    @Deprecated
    default LzhMetaObjectHandler setInsertFieldValByName(String fieldName, Object fieldVal, MetaObject metaObject) {
        return this.setFieldValByName(fieldName, fieldVal, metaObject, FieldFill.INSERT);
    }

    /** @deprecated */
    @Deprecated
    default LzhMetaObjectHandler setUpdateFieldValByName(String fieldName, Object fieldVal, MetaObject metaObject) {
        return this.setFieldValByName(fieldName, fieldVal, metaObject, FieldFill.UPDATE);
    }

    /** @deprecated */
    @Deprecated
    default LzhMetaObjectHandler setFieldValByName(String fieldName, Object fieldVal, MetaObject metaObject, FieldFill fieldFill) {
        if (Objects.nonNull(fieldVal) && this.isFill(fieldName, fieldVal, metaObject, fieldFill)) {
            metaObject.setValue(fieldName, fieldVal);
        }

        return this;
    }

    /** @deprecated */
    @Deprecated
    default boolean isFill(String fieldName, Object fieldVal, MetaObject metaObject, FieldFill fieldFill) {
        Optional<TableFieldInfo> first = this.findTableInfo(metaObject).getFieldList().stream().filter((e) -> {
            return e.getProperty().equals(fieldName) && e.getPropertyType().isAssignableFrom(fieldVal.getClass());
        }).findFirst();
        if (!first.isPresent()) {
            return false;
        } else {
            FieldFill fill = ((TableFieldInfo)first.get()).getFieldFill();
            return fill == fieldFill || FieldFill.INSERT_UPDATE == fill;
        }
    }

    default TableInfo findTableInfo(MetaObject metaObject) {
        return TableInfoHelper.getTableInfo(metaObject.getOriginalObject().getClass());
    }

    default <T, E extends T> LzhMetaObjectHandler strictInsertFill(MetaObject metaObject, String fieldName, Class<T> fieldType, E fieldVal) {
        return this.strictInsertFill(this.findTableInfo(metaObject), metaObject, Collections.singletonList(StrictFill.of(fieldName, fieldType, fieldVal)));
    }

    default <T, E extends T> LzhMetaObjectHandler strictInsertFill(MetaObject metaObject, String fieldName, Supplier<E> fieldVal, Class<T> fieldType) {
        return this.strictInsertFill(this.findTableInfo(metaObject), metaObject, Collections.singletonList(StrictFill.of(fieldName, fieldVal, fieldType)));
    }

    default LzhMetaObjectHandler strictInsertFill(TableInfo tableInfo, MetaObject metaObject, List<StrictFill<?, ?>> strictFills) {
        return this.strictFill(true, tableInfo, metaObject, strictFills);
    }

    default <T, E extends T> LzhMetaObjectHandler strictUpdateFill(MetaObject metaObject, String fieldName, Supplier<E> fieldVal, Class<T> fieldType) {
        return this.strictUpdateFill(this.findTableInfo(metaObject), metaObject, Collections.singletonList(StrictFill.of(fieldName, fieldVal, fieldType)));
    }

    default <T, E extends T> LzhMetaObjectHandler strictUpdateFill(MetaObject metaObject, String fieldName, Class<T> fieldType, E fieldVal) {
        return this.strictUpdateFill(this.findTableInfo(metaObject), metaObject, Collections.singletonList(StrictFill.of(fieldName, fieldType, fieldVal)));
    }

    default LzhMetaObjectHandler strictUpdateFill(TableInfo tableInfo, MetaObject metaObject, List<StrictFill<?, ?>> strictFills) {
        return this.strictFill(false, tableInfo, metaObject, strictFills);
    }

    default LzhMetaObjectHandler strictFill(boolean insertFill, TableInfo tableInfo, MetaObject metaObject, List<StrictFill<?, ?>> strictFills) {
        if (insertFill && tableInfo.isWithInsertFill() || !insertFill && tableInfo.isWithUpdateFill()) {
            strictFills.forEach((i) -> {
                String fieldName = i.getFieldName();
                Class<?> fieldType = i.getFieldType();
                tableInfo.getFieldList().stream().filter((j) -> {
                    return j.getProperty().equals(fieldName) && fieldType.equals(j.getPropertyType()) && (insertFill && j.isWithInsertFill() || !insertFill && j.isWithUpdateFill());
                }).findFirst().ifPresent((j) -> {
                    this.strictFillStrategy(metaObject, fieldName, i.getFieldVal());
                });
            });
        }

        return this;
    }

    default LzhMetaObjectHandler fillStrategy(MetaObject metaObject, String fieldName, Object fieldVal) {
        if (this.getFieldValByName(fieldName, metaObject) == null) {
            this.setFieldValByName(fieldName, fieldVal, metaObject);
        }

        return this;
    }

    default LzhMetaObjectHandler strictFillStrategy(MetaObject metaObject, String fieldName, Supplier<?> fieldVal) {
        if (metaObject.getValue(fieldName) == null) {
            Object obj = fieldVal.get();
            if (Objects.nonNull(obj)) {
                metaObject.setValue(fieldName, obj);
            }
        }

        return this;
    }
}
